/*
 *  Copyright (c) GBA-NCTI-ISDC. 2022-2024.
 *
 *  openGauss DataKit is licensed under Mulan PSL v2.
 *  You can use this software according to the terms and conditions of the Mulan PSL v2.
 *  You may obtain a copy of Mulan PSL v2 at:
 *
 *  http://license.coscl.org.cn/MulanPSL2
 *
 *  THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 *  EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 *  MERCHANTABILITY OR FITFOR A PARTICULAR PURPOSE.
 *  See the Mulan PSL v2 for more details.
 *  -------------------------------------------------------------------------
 *
 *  DataSourceConfig.java
 *
 *  IDENTIFICATION
 *  plugins/observability-instance/src/main/java/com/nctigba/observability/instance/config/DataSourceConfig.java
 *
 *  -------------------------------------------------------------------------
 */

package com.nctigba.observability.instance.config;

import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.druid.DruidDataSourceCreator;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.gitee.starblues.bootstrap.PluginContextHolder;
import com.gitee.starblues.spring.environment.EnvironmentProvider;
import com.nctigba.observability.instance.enums.DbDataLocationEnum;
import org.opengauss.admin.common.exception.CustomException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
import org.springframework.boot.sql.init.DatabaseInitializationMode;
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;

/**
 * @author LZW
 * @date 2023/1/5
 */
@Configuration
public class DataSourceConfig {
    static final String GS_DRIVER = "org.opengauss.Driver";
    static final String SQLITE_DRIVER = "org.sqlite.JDBC";
    static final String DB_PATH = "data/observability-instance-data.db";
    static final String SQLITE_URL = "jdbc:sqlite:" + DB_PATH;

    @Autowired
    DynamicDataSourceProperties properties;
    @Autowired
    DruidDataSourceCreator druidDataSourceCreator;

    /**
     * primary dataSource
     *
     * @return DataSource
     * @throws IOException e
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    @Profile("!dev")
    public DataSource dataSource() throws IOException {
        EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider();
        // read config from dataKit platform
        DataSourceProperty primaryProperty = new DataSourceProperty();
        primaryProperty.setUrl(environmentProvider.getString("spring.datasource.url"));
        primaryProperty.setUsername(environmentProvider.getString("spring.datasource.username"));
        primaryProperty.setPassword(environmentProvider.getString("spring.datasource.password"));
        String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name");
        primaryProperty.setDriverClassName(driverClassName);
        DataSource primary = druidDataSourceCreator.createDataSource(primaryProperty);
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(new ArrayList<>());
        dataSource.addDataSource("primary", primary);
        dataSource.setPrimary("primary");
        DataSourceProperty embeddedProerty = properties.getDatasource().get("embedded");
        if (GS_DRIVER.equals(driverClassName)) {
            embeddedProerty.setDriverClassName(SQLITE_DRIVER);
            embeddedProerty.setUrl(SQLITE_URL);
            initDbFile();
        }
        DataSource embedded = druidDataSourceCreator.createDataSource(embeddedProerty);
        dataSource.addDataSource("embedded", embedded);
        return dataSource;
    }

    /**
     * DataSourceScriptDatabaseInitializer
     *
     * @param dataSource DataSource
     * @return DataSourceScriptDatabaseInitializer
     */
    @Bean("instanceDataSourceScriptDatabaseInitializer")
    @Profile("!dev")
    public DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource) {
        EnvironmentProvider environmentProvider = PluginContextHolder.getEnvironmentProvider();
        String driverClassName = environmentProvider.getString("spring.datasource.driver-class-name");
        Optional<DbDataLocationEnum> optional = DbDataLocationEnum.of(driverClassName);
        if (!(dataSource instanceof DynamicRoutingDataSource)) {
            throw new CustomException("datasource is not DynamicRoutingDataSource");
        }
        DynamicRoutingDataSource drds = (DynamicRoutingDataSource) dataSource;
        DataSource primary = drds.getDataSource("primary");
        DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
        settings.setContinueOnError(true);
        settings.setSeparator(";");
        settings.setMode(DatabaseInitializationMode.ALWAYS);
        if (optional.isEmpty()) {
            return new DataSourceScriptDatabaseInitializer(primary, new DatabaseInitializationSettings());
        }
        DbDataLocationEnum dataLocationEnum = optional.get();
        settings.setDataLocations(dataLocationEnum.getLocations());
        return new DataSourceScriptDatabaseInitializer(primary, settings);
    }

    private void initDbFile() throws IOException {
        File dbFile = new File(DB_PATH);
        File dbDir = dbFile.getParentFile();
        if (!dbDir.exists()) {
            dbDir.mkdirs();
        }
        if (!dbFile.exists()) {
            dbFile.createNewFile();
        }
    }

    /**
     * embeddedDataSourceScriptDatabaseInitializer
     *
     * @param dataSource DataSource
     * @return DataSourceScriptDatabaseInitializer
     */
    @Bean("embeddedDataSourceScriptDatabaseInitializer")
    @Profile("!dev")
    public DataSourceScriptDatabaseInitializer embeddedDataSourceScriptDatabaseInitializer(DataSource dataSource) {
        if (!(dataSource instanceof DynamicRoutingDataSource)) {
            throw new CustomException("datasource is not DynamicRoutingDataSource");
        }
        DynamicRoutingDataSource drds = (DynamicRoutingDataSource) dataSource;
        DataSource embedded = drds.getDataSource("embedded");
        DataSourceProperty property = properties.getDatasource().get("embedded");
        DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
        settings.setContinueOnError(property.getInit().isContinueOnError());
        settings.setSeparator(property.getInit().getSeparator());
        settings.setMode(DatabaseInitializationMode.ALWAYS);
        settings.setDataLocations(Arrays.asList(property.getInit().getData()));
        settings.setSchemaLocations(Arrays.asList(property.getInit().getSchema()));
        return new DataSourceScriptDatabaseInitializer(embedded, settings);
    }
}